//@author A0101119Y
#include "GCalRequestAgent.h"

using namespace Poco::Net;
using namespace Poco;
using namespace Twentyfifth;
using namespace std;

const string GCalRequestAgent::GCAL_HOSTNAME = "https://www.googleapis.com";
const string GCalRequestAgent::GCAL_API_CALENDAR = "/calendar/v3";
const string GCalRequestAgent::GCAL_PATH_ADDCALENDAR = "/calendars";
const string GCalRequestAgent::GCAL_PATH_CALENDARLIST = "/users/me/calendarList";
const string GCalRequestAgent::GCAL_PATH_CALENDAR = "/users/me/calendars";
const string GCalRequestAgent::GCAL_PATH_EVENTS = "/calendars/%s/events";
const string GCalRequestAgent::GCAL_PATH_QUICKADD = "/quickAdd";
const string GCalRequestAgent::HEADER_AUTHORIZATION = "Authorization";
const string GCalRequestAgent::HEADER_CONTENT_TYPE = "Content-Type";
const string GCalRequestAgent::VALUE_JSON = "application/json";
const string GCalRequestAgent::VALUE_BEARER = "Bearer ";
const string GCalRequestAgent::LABEL_ACCESS_TOKEN = "access_token";
const string GCalRequestAgent::LABEL_GCAL_ID = "id";
const string GCalRequestAgent::LABEL_CALENDAR_ID = "calendar_id";
const string GCalRequestAgent::LABEL_CALENDAR_NAME = "summary";
const string GCalRequestAgent::LABEL_CALENDAR_TIMEZONE = "timeZone";
const string GCalRequestAgent::LABEL_ITEMS = "items";
const string GCalRequestAgent::PARAM_GCAL_QUICKADD = "?text=";
const string GCalRequestAgent::PARAM_SINGLE_EVENTS = "?singleEvents=true&showDeleted=true";
const string GCalRequestAgent::PLACEHOLDER_STRING = "%s";
const string GCalRequestAgent::CHAR_SPACE = " ";
const string GCalRequestAgent::CHAR_PLUS = "+";

string GCalRequestAgent::getCalendars() {
	string fullUri = GCAL_HOSTNAME + GCAL_API_CALENDAR + GCAL_PATH_CALENDARLIST;
	string accessToken = variablemgr.getValue(LABEL_ACCESS_TOKEN);
	setURI(fullUri);
	initSession();
	request.add(HEADER_AUTHORIZATION, VALUE_BEARER + accessToken);
	setRequestMethod(METHOD_GET);
	sendRequest();
	if (responseCode == 200) {
		return responseContent;
	} else if (responseCode == 401) {
		throw Poco::InvalidAccessException(401);
	} else {
		throw Poco::InvalidAccessException();
	}
}

string GCalRequestAgent::addCalendar(string name) {
	stringstream requestss;
	GCalParser parser;
	string fullUri = GCAL_HOSTNAME + GCAL_API_CALENDAR + GCAL_PATH_ADDCALENDAR;
	string accessToken = variablemgr.getValue(LABEL_ACCESS_TOKEN);
	string userTimeZone = variablemgr.getValue(LABEL_CALENDAR_TIMEZONE);
	request.add(HEADER_AUTHORIZATION, VALUE_BEARER + accessToken);
	request.add(HEADER_CONTENT_TYPE, VALUE_JSON);
	setURI(fullUri);
	initSession();
	map<string, string> requestBodyMap;
	requestBodyMap[LABEL_CALENDAR_NAME] = name;
	requestBodyMap[LABEL_CALENDAR_TIMEZONE] = userTimeZone;
	parser.parseMapToObject(requestBodyMap).stringify(requestss);
	setRequestContent(requestss.str());
	setRequestMethod(METHOD_POST);
	sendRequest();
	if (responseCode == 401) {
		throw Poco::InvalidAccessException("Unauthorized at addCalendar.", 401);
	}
	try {
		map<string, string> addResponse = parser.parseJsonToMap(responseContent);
		string calendarId = addResponse.at(LABEL_GCAL_ID);
		return calendarId;
	}
	catch (Exception &e) {
		throw Poco::InvalidAccessException(e.displayText(), e.code());
	}
	// variablemgr.setValue(LABEL_CALENDAR_ID, calendarId); for add
}

string GCalRequestAgent::getEvents(string calId) {
	GCalParser parser;
	string fullUri = GCAL_HOSTNAME + GCAL_API_CALENDAR + GCAL_PATH_EVENTS + PARAM_SINGLE_EVENTS;
	size_t posReplace = fullUri.find(PLACEHOLDER_STRING);
	fullUri.replace(posReplace, PLACEHOLDER_STRING.length(), calId);
	string accessToken = variablemgr.getValue(LABEL_ACCESS_TOKEN);
	setURI(fullUri);
	initSession();
	request.add(HEADER_AUTHORIZATION, VALUE_BEARER + accessToken);
	setRequestMethod(METHOD_GET);
	sendRequest();
	if (responseCode == 401) {
		throw Poco::InvalidAccessException("Unauthorized at getEvents.", 401);
	}
	try {
		map<string, string> getResponse = parser.parseJsonToMap(responseContent);
		return getResponse.at(LABEL_ITEMS);
	}
	catch (Exception &e) {
		throw Poco::InvalidAccessException(e.displayText(), e.code());
	}
}

string GCalRequestAgent::addEvent(string calId, Task t) {
	GCalParser parser;
	stringstream requestStream;
	string userTimeZone = variablemgr.getValue(LABEL_CALENDAR_TIMEZONE);
	parser.setTimeZone(userTimeZone);
	Poco::JSON::Object taskObject = parser.parseTaskToObject(t);
	taskObject.stringify(requestStream);
	setRequestContent(requestStream.str());
	string fullUri = GCAL_HOSTNAME + GCAL_API_CALENDAR + GCAL_PATH_EVENTS;
	size_t posReplace = fullUri.find(PLACEHOLDER_STRING);
	fullUri.replace(posReplace, PLACEHOLDER_STRING.length(), calId);
	string accessToken = variablemgr.getValue(LABEL_ACCESS_TOKEN);
	request.add(HEADER_AUTHORIZATION, VALUE_BEARER + accessToken);
	request.add(HEADER_CONTENT_TYPE, VALUE_JSON);
	setURI(fullUri);
	initSession();
	setRequestMethod(METHOD_POST);
	sendRequest();
	if (responseCode == 401) {
		throw Poco::InvalidAccessException("Unauthorized at addEvent.", 401);
	}
	try {
		return responseContent;
	}
	catch (Exception &e) {
		throw Poco::InvalidAccessException(e.displayText(), e.code());
	}
}

void GCalRequestAgent::deleteEvent(string calId, Task t) {
	GCalParser parser;
	string uriEventId = "/" + t.getGCalId();
	string fullUri = GCAL_HOSTNAME + GCAL_API_CALENDAR + GCAL_PATH_EVENTS + uriEventId;
	size_t posReplace = fullUri.find(PLACEHOLDER_STRING);
	fullUri.replace(posReplace, PLACEHOLDER_STRING.length(), calId);
	string accessToken = variablemgr.getValue(LABEL_ACCESS_TOKEN);
	request.add(HEADER_AUTHORIZATION, VALUE_BEARER + accessToken);
	request.add(HEADER_CONTENT_TYPE, VALUE_JSON);
	setURI(fullUri);
	initSession();
	setRequestMethod(METHOD_DELETE);
	sendRequest();

	if (responseCode == 401) {
		throw Poco::InvalidAccessException("Unauthorized at deleteEvent.", 401);
	}
	return;
}

void GCalRequestAgent::editEvent(string calId, Task t) {
	GCalParser parser;
	string userTimeZone = variablemgr.getValue(LABEL_CALENDAR_TIMEZONE);
	parser.setTimeZone(userTimeZone);
	string uriEventId = "/" + t.getGCalId();
	string fullUri = GCAL_HOSTNAME + GCAL_API_CALENDAR + GCAL_PATH_EVENTS + uriEventId;
	size_t posReplace = fullUri.find(PLACEHOLDER_STRING);
	fullUri.replace(posReplace, PLACEHOLDER_STRING.length(), calId);
	string accessToken = variablemgr.getValue(LABEL_ACCESS_TOKEN);
	request.add(HEADER_AUTHORIZATION, VALUE_BEARER + accessToken);
	request.add(HEADER_CONTENT_TYPE, VALUE_JSON);
	setURI(fullUri);
	initSession();
	stringstream requestStream;
	Poco::JSON::Object taskObject = parser.parseTaskToObject(t);
	taskObject.stringify(requestStream);
	setRequestContent(requestStream.str());
	setRequestMethod(METHOD_PATCH);
	sendRequest();
	// TODO: need to deal with response content
	if (responseCode == 401) {
		throw Poco::InvalidAccessException("Unauthorized at editEvent.", 401);
	} else if (responseCode != 200) {
		throw Poco::InvalidAccessException();
	}
	return;
}

string GCalRequestAgent::quickAddEvent(string calId, string input) {
	GCalParser parser;
	string fullUri = GCAL_HOSTNAME + GCAL_API_CALENDAR + GCAL_PATH_EVENTS + GCAL_PATH_QUICKADD;
	fullUri += PARAM_GCAL_QUICKADD + input;
	size_t posReplaceSpaces = fullUri.find(CHAR_SPACE);
	while (posReplaceSpaces != string::npos) {
		fullUri.replace(posReplaceSpaces, CHAR_SPACE.length(), CHAR_PLUS);
		posReplaceSpaces = fullUri.find(CHAR_SPACE);
	}
	size_t posReplace = fullUri.find(PLACEHOLDER_STRING);
	fullUri.replace(posReplace, PLACEHOLDER_STRING.length(), calId);
	string accessToken = variablemgr.getValue(LABEL_ACCESS_TOKEN);
	request.add(HEADER_AUTHORIZATION, VALUE_BEARER + accessToken);
	request.add(HEADER_CONTENT_TYPE, VALUE_JSON);
	setURI(fullUri);
	initSession();
	setRequestMethod(METHOD_POST);
	sendRequest();
	if (responseCode == 401) {
		throw Poco::InvalidAccessException("Unauthorized at addEvent.", 401);
	}
	try {
		return responseContent;
	}
	catch (Exception &e) {
		throw Poco::InvalidAccessException(e.displayText(), e.code());
	}
}
